package scrumtaculous.domain;

import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.jdbc.JdbcUpdateAffectedIncorrectNumberOfRowsException;
import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;

/**
 * This is a super class for all Data Access Objects in our domain.
 * 
 * We'll put all of the typical CRUD methods here, anything special
 * will have to be created in the sub-class.
 */
public abstract class DataAccessObject extends SqlMapClientDaoSupport
{

   protected String daoName;
   protected Log log = LogFactory.getLog(DataAccessObject.class);

   public DataAccessObject()
   {
      if (log.isTraceEnabled())
      {
         log.trace("");
      }
   }

   @SuppressWarnings("unchecked")
   public List find()
   {
      return getSqlMapClientTemplate().queryForList(daoName + ".find");
   }

   @SuppressWarnings("unchecked")
   public List find(DataTransferObject dto)
   {
      String queryStatementName = daoName + ".findByExample";
      log.debug("queryStatementName: " + queryStatementName);

      return getSqlMapClientTemplate().queryForList(queryStatementName, dto);
   }

   public DataTransferObject findById(Integer id)
   {
      return (DataTransferObject) getSqlMapClientTemplate().queryForObject(daoName + ".findById", id);
   }

   public DataTransferObject save(DataTransferObject dto)
   {
      log.debug(dto.toString());
      if (log.isTraceEnabled())
      {
         log.trace(dto);
      }

      if (dto.isNew())
      {
         log.debug("create");
         dto.setVersion(0);
         Integer newId;

         newId = (Integer) getSqlMapClientTemplate().insert(daoName + ".insert", dto);
         dto.setId(newId);
         return dto;
      }

      log.debug("update");
      try
      {
         getSqlMapClientTemplate().update(daoName + ".update", dto, 1);
         dto.setVersion(dto.getVersion() + 1);
         return dto;
      }
      catch (JdbcUpdateAffectedIncorrectNumberOfRowsException exception)
      {
         log.warn(exception.getMessage());
         throw new OptimisticLockingFailureException(
               "Provided you have given an object with a valid ID, someone has written underneath you.  "
                     + "Do another find and save.", exception);
      }
   }

   protected void delete(DataTransferObject dto)
   {
      if (log.isTraceEnabled())
      {
         log.trace(dto);
      }

      if (dto == null)
      {
         String message = "can't delete a null object";
         log.warn(message);
         throw new RuntimeException(message);
      }

      if (!dto.cameFromDatabase())
      {
         throw new RuntimeException("can only delete object from database after reading it from database");
      }

      try
      {
         getSqlMapClientTemplate().delete(daoName + ".delete", dto.getId(), 1);
      }
      catch (JdbcUpdateAffectedIncorrectNumberOfRowsException e)
      {
         String message = "The delete failed on " + dto + ".  " + e.getMessage();
         log.warn(message);
         throw new RuntimeException(message, e);
      }
   }

   public static Object returnOnlyOne(String name, List<Object> objects)
   {
      if (objects.size() != 1)
      {
         throw new RuntimeException("should have only one " + name + " but " + objects.size() + " were found");
      }
      return objects.get(0);
   }

   //   public String getDaoName()
   //   {
   //      return daoName;
   //   }

   @Required
   public void setDaoName(String daoNameIn)
   {
      daoName = daoNameIn;
   }
}
